package com.sanshengshui.netty.oioAndnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author zyl
 * @description
 * @since 2024/7/17
 */
public class PlainNioServer {
    public static void serve(int port) throws IOException {
        //1.创建和配置服务器通道
        //打开一个 ServerSocketChannel（服务器套接字通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //配置通道为非阻塞模式（configureBlocking(false)），这样通道的操作不会阻塞
        serverChannel.configureBlocking(false);
        //2.绑定端口
        //获取服务器套接字（ServerSocket）并将其绑定到指定的端口（port）
        ServerSocket ssocket = serverChannel.socket();
        InetSocketAddress address = new InetSocketAddress(port);
        ssocket.bind(address);
        //3.创建选择器并注册通道
        //创建一个 Selector（选择器）并将服务器通道注册到选择器上，关注 OP_ACCEPT（接受连接）事件
        Selector selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        //4.准备发送给客户端的消息
        //final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".ge
        // 创建一个包含消息的 ByteBuffer，将要发送的字符串转换为字节数组并包装到缓冲区中
        final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
        //5.主循环
        //进入一个无限循环，调用 selector.select() 方法来等待事件。如果发生 IOException 异常，则打印异常信息并跳出循环
        for (; ; ) {
            try {
                selector.select();
            } catch (IOException ex) {
                ex.printStackTrace();
                // handle exception
                break;
            }
            //6.处理就绪的键
            //获取所有已就绪的选择键（SelectionKey），并使用迭代器进行遍历。每次处理完一个键后，从集合中移除它
            Set<SelectionKey> readyKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                try {
                    //7.处理连接和读写事件
                    //如果键的事件是 OP_ACCEPT，表示有新的客户端连接请求
                    if (key.isAcceptable()) {
                        //接受客户端连接，获取 SocketChannel（客户端套接字通道）
                        ServerSocketChannel server =
                                (ServerSocketChannel) key.channel();
                        SocketChannel client = server.accept();
                        //配置客户端通道为非阻塞模式
                        client.configureBlocking(false);
                        //将客户端通道注册到选择器上，关注 OP_WRITE 和 OP_READ 事件，并附加要发送的消息副本
                        client.register(selector, SelectionKey.OP_WRITE |
                                SelectionKey.OP_READ, msg.duplicate());
                        System.out.println(
                                "Accepted connection from " + client);
                    }
                    //如果键的事件是 OP_WRITE，表示通道准备好写数据：
                    if (key.isWritable()) {
                        //获取客户端通道和附加的缓冲区。
                        SocketChannel client =
                                (SocketChannel) key.channel();
                        //写入数据到客户端通道，直到缓冲区中的数据写完或写操作返回0
                        ByteBuffer buffer =
                                (ByteBuffer) key.attachment();
                        while (buffer.hasRemaining()) {
                            if (client.write(buffer) == 0) {
                                break;
                            }
                        }
                        client.close();
                    }
                } catch (IOException ex) {
                    key.cancel();
                    try {
                        key.channel().close();
                    } catch (IOException cex) {
                        // ignore on close
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        try {
            serve(8080);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}